#include "parser.h"

using namespace Asukai::json;

Parser::Parser() : m_idx(0), m_str("")
{
}

void Parser::load(const std::string &str)
{
    m_str = str;
}

Json Parser::parse()
{
    char ch = get_next_token();
    switch (ch)
    {
    case 'n':
    {
        // m_idx--;
        return parse_null();
    }
    case 't':
    case 'f':
    {
        // m_idx--;
        return parse_bool();
    }
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        return parse_number();
    case '\"':
        return parse_string(); // Json(parse_string())
    case '[':
        return parse_array();
    case '{':
        return parse_object();
    default:
        break;
    }
    throw new std::logic_error("unexcepted string!");
}

void Parser::skip_write_space()
{
    while (m_str[m_idx] == ' ' || m_str[m_idx] == '\n' || m_str[m_idx] == '\t' || m_str[m_idx] == '\r')
    {
        m_idx++;
    }
}

char Parser::get_next_token()
{
    skip_write_space();
    return m_str[m_idx];
}

Json Parser::parse_null()
{
    if (m_str.compare(m_idx, 4, "null") == 0)
    {
        m_idx += 4;
        return Json();
    }
    throw new std::logic_error("parse null error");
}

Json Parser::parse_bool()
{
    if (m_str.compare(m_idx, 4, "true") == 0)
    {
        m_idx += 4;
        return Json(true);
    }
    else if (m_str.compare(m_idx, 5, "false") == 0)
    {
        m_idx += 5;
        return Json(false);
    }
    throw new std::logic_error("parse bool error");
}

Json Parser::parse_number()
{
    int pos = m_idx;
    // bool isNegativeNumber = false;

    if (m_str[m_idx] == '-')
    {
        m_idx++;
        // isNegativeNumber = true;
    }

    if (m_str[m_idx] == '0')
    {
        m_idx++;
    }
    else
    {
        if (!(m_str[m_idx] > '0' && m_str[m_idx] <= '9'))
        {
            throw new std::logic_error("parse number error");
        }
        while (m_str[m_idx] > '0' && m_str[m_idx] <= '9')
        {
            m_idx++;
        }
    }

    if (m_str[m_idx] != '.')
    {
        int i = std::atoi(m_str.c_str() + pos);
        // if (isNegativeNumber)
        // {
        //     i = -i;
        // }
        return Json(i);
    }
    m_idx++;

    if (!(m_str[m_idx] >= '0' && m_str[m_idx] <= '9'))
    {
        throw new std::logic_error("parse number error");
    }
    while (m_str[m_idx] >= '0' && m_str[m_idx] <= '9')
    {
        m_idx++;
    }
    if (m_str[m_idx] == 'e' || m_str[m_idx] == 'E')
    {
        m_idx++;
        if (m_str[m_idx] == '+' || m_str[m_idx] == '-')
        {
            m_idx++;
        }
        if (!(m_str[m_idx] > '0' && m_str[m_idx] <= '9'))
        {
            throw new std::logic_error("parse number error");
        }
        while (m_str[m_idx] >= '0' && m_str[m_idx] <= '9')
        {
            m_idx++;
        }
    }
    double f = std::atof(m_str.c_str() + pos);
    // if (isNegativeNumber)
    // {
    //     f = -f;
    // }
    return Json(f);
}

std::string Parser::parse_string()
{
    m_idx++;
    int pos = m_idx;

    while (true)
    {
        if (m_str[m_idx] == '\"')
        {
            break;
        }
        m_idx++;
    }
    m_idx++;
    skip_write_space();
    return std::string(m_str.begin() + pos, m_str.begin() + m_idx - 1);

}

Json Parser::parse_array()
{
    Json array(Json::Type::json_array);
    char ch = m_str[++m_idx];
    if (ch == ']')
    {
        return array;
    }
    while (true)
    {
        skip_write_space();
        array.append(parse());
        ch = m_str[m_idx];
        if (ch == ']')
        {
            break;
        }
        if (ch != ',')
        {
            throw new std::logic_error("parse array error");
        }
        m_idx++;
    }
    m_idx++;
    skip_write_space();
    return array;
}

Json Parser::parse_object()
{
    Json object(Json::Type::json_object);
    char ch = m_str[++m_idx];
    if (ch == '}')
    {
        return object;
    }
    while (true)
    {
        skip_write_space();
        ch = m_str[m_idx];
        if (ch != '\"')
        {
            throw new std::logic_error("parse object error");
        }
        std::string key = parse();

        skip_write_space();
        ch = m_str[m_idx++];
        if (ch != ':')
        {
            throw new std::logic_error("parse object error");
        }

        object[key] = parse();
        skip_write_space();

        ch = m_str[m_idx];
        if (ch == '}')
        {
            break;
        }

        if (ch != ',')
        {
            throw new std::logic_error("parse object error");
        }
        m_idx++;
    }
    m_idx++;
    skip_write_space();

    return object;
}
